﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework;

namespace NDF.SkinnedModelProcessor
{
	internal class SkinnedProcessorHelper
	{
		/// <summary>
		/// Bakes unwanted transforms into the model geometry,
		/// so everything ends up in the same coordinate system.
		/// </summary>
		internal static void FlattenTransforms(NodeContent node, BoneContent skeleton)
		{
			foreach (NodeContent child in node.Children)
			{
				// Don't process the skeleton, because that is special.
				if (child == skeleton)
					continue;

				// Bake the local transform into the actual geometry.
				MeshHelper.TransformScene(child, child.Transform);

				// Having baked it, we can now set the local
				// coordinate system back to identity.
				child.Transform = Matrix.Identity;

				// Recurse.
				FlattenTransforms(child, skeleton);
			}
		}

		/// <summary>
		/// Makes sure this mesh contains the kind of data we know how to animate.
		/// </summary>
		internal static void ValidateMesh(NodeContent node, ContentProcessorContext context,
								 string parentBoneName)
		{
			MeshContent mesh = node as MeshContent;

			if (mesh != null)
			{
				// Validate the mesh.
				if (parentBoneName != null)
				{
					context.Logger.LogWarning(null, null,
						"Mesh {0} is a child of bone {1}. SkinnedModelProcessor " +
						"does not correctly handle meshes that are children of bones.",
						mesh.Name, parentBoneName);
				}

				if (!MeshHasSkinning(mesh))
				{
					context.Logger.LogWarning(null, null,
						"Mesh {0} has no skinning information, so it has been deleted.",
						mesh.Name);

					mesh.Parent.Children.Remove(mesh);
					return;
				}
			}
			else if (node is BoneContent)
			{
				// If this is a bone, remember that we are now looking inside it.
				parentBoneName = node.Name;
			}

			// Recurse (iterating over a copy of the child collection is a bad idea,
			// because validating children may delete some of them).
			foreach (NodeContent child in new List<NodeContent>(node.Children))
				ValidateMesh(child, context, parentBoneName);
		}

		/// <summary>
		/// Checks whether a mesh contains skininng information.
		/// </summary>
		internal static bool MeshHasSkinning(MeshContent mesh)
		{
			foreach (GeometryContent geometry in mesh.Geometry)
			{
				if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights()))
					return false;
			}

			return true;
		}
	}
}
